/**
 * This file is a part of qloudgen-sec. 
 * You can redistribute qloudgen-sec and/or modify it under the terms of the Lesser GNU General Public License version 3. 
 * qloudgen-sec is distributed WITHOUT ANY WARRANTY. 
 *
 * See the Lesser GNU General Public License for more details at http://www.gnu.org/licenses/. 
 **/
package org.redisson.cluster;

/**
 * <p>Title: Redis cluster key-slots support</p>
 * <p>Description: CRC16 (XModem) implementation according to CCITT standards.</p>
 * <p>Copyright: qloudgen/sec 2014</p>
 * @author <a href="mailto:hyq.dd.sun@gmail.com">glue.3M</a>
 */
public final class ClusterCRC16 {
	/**
	 * Default constructor.
	 */
	private ClusterCRC16() {
	}
	
	/**
	 * Returns current hash slot value.
	 *
	 * @param key
	 * @return int
	 */
	public static int getSlot(byte[] key) {
		return getSlot( new String( key ) );
	}
	
	/**
	 * Returns current hash slot value.
	 *
	 * 1)If the key contains a { character.
	 * 2)There is a } character on the right of {.
	 * 3)There are one or more characters between the first occurrence of { and the first occurrence of } after the first occurrence of {.
	 * Then instead of hashing the key, only what is between the first occurrence of { and the first occurrence of } on its right are hashed.
	 *
	 * @param key
	 * @return int
	 */
	public static int getSlot(String key) {
		int s = key.indexOf( "{" );
		if ( s > -1 ) {
			int e = key.indexOf( "}" , s+1 );
			if ( e > -1 && e != s+1 ) {
				key = key.substring( s+1 , e );
			}
		}
		//
		return getCRC16( key ) % 16384;
	}

	/**
	 * Returns current hash slot value.
	 *
	 * @param key
	 * @return int
	 */
    private static int getCRC16(String key) {
    	// Initial value.
		int crc = 0x0000;
		// Represents x^16+x^12+x^5+1
		int polynomial = 0x1021;
		/**
		 * Default charset ???
		 */
		for ( byte b : key.getBytes() ) {
			for ( int i = 0 ; i < 8 ; i++ ) {
				boolean bit = ( ( b >> ( 7 - i ) & 1 ) == 1 );
				boolean c15 = ( ( crc >> 15 & 1 ) == 1 );
				crc <<= 1;
				/**
				 * If coefficient of bit and remainder polynomial = 1 xor crc with polynomial.
				 */
				if ( c15 ^ bit ) {
					crc ^= polynomial;
				}
			}
		}
		//
		return crc &= 0xffff ;
    }
}